package models

import (
	"errors"
	"fmt"
	"gitee.com/lzqysoft/ims/libs/cache"
	"gitee.com/lzqysoft/ims/libs/params"
	"reflect"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
)

type User struct {
	Id           string         `orm:"column(id);pk"`
	Name         string         `orm:"unique;size(64)"`
	Pwd          string         `orm:"column(pwd)"`
	Organization *Organization  `orm:"rel(fk)"`
	Type         int            `orm:"column(type)"`
	Sex          int            `orm:"column(sex)"`
	Phone        string         `orm:"column(phone);null"`
	Mobile       string         `orm:"column(mobile);null"`
	Contact      string         `orm:"column(contact);null"`
	Title        string         `orm:"column(title);null"`
	Abbr         string         `orm:"column(abbr);null"`
	Job          string         `orm:"column(job);null"`
	Photo        string         `orm:"column(photo);null"`
	Email        string         `orm:"column(email);null"`
	Logintime    time.Time      `orm:"column(logintime);type(timestamp with time zone);null"`
	Loginip      string         `orm:"column(loginip);null"`
	Logincount   int            `orm:"column(logincount)"`
	Activecount  int            `orm:"column(activecount)"`
	Status       int            `orm:"column(status);default(1)"`
	Remark       string         `orm:"column(remark);null"`
	Isdelete     int            `orm:"column(isdelete);default(0)"`
	Ctime        time.Time      `orm:"column(ctime);type(timestamp with time zone);auto_now_add"`
	Creater      string         `orm:"column(creater);default('admin')"`
	Utime        time.Time      `orm:"column(utime);type(timestamp with time zone);auto_now"`
	Updater      string         `orm:"column(updater);default('admin')"`
	Salt         string         `orm:"column(salt);default('')"`
	JdType       int            `orm:"column(jd_type)"`
	Dept         []*Dept        `orm:"rel(m2m)"`
	Role         []*Role        `orm:"rel(m2m)"`
	Message      []*Message     `orm:"reverse(many)"`
	Livechannel  []*Livechannel `orm:"rel(m2m)"`
	Video        []*Video       `orm:"rel(m2m)"`
}

func (t *User) TableName() string {
	return "sys_user"
}

func init() {
	orm.RegisterModel(new(User))
}

// 新增用户
// last inserted Id on success.
func AddUser(m *User) (id int64, err error) {
	o := orm.NewOrm()
	m.Status = 1

	id, err = o.Insert(m)
	return
}

// GetUserById retrieves User by Id. Returns error if
// Id doesn't exist
func GetUserById(id string) (v *User, err error) {
	// cid := "GetUserById.id." + id
	// var user User
	// err = cache.GetCache(cid, &user)
	// if err == nil {
	// 	return &user, nil
	// } else {
	o := orm.NewOrm()
	v = &User{Id: id}
	if err = o.Read(v); err != nil {
		return nil, err
	}
	if _, err := o.LoadRelated(v, "Role"); err != nil {
		return nil, err
	}
	if _, err = o.LoadRelated(v, "Dept"); err != nil {
		return nil, err
	}
	if _, err = o.LoadRelated(v, "Organization"); err != nil {
		return nil, err
	}
	//o.LoadRelated(v, "Video")
	//o.LoadRelated(v, "Livechannel")
	// 	cacheExpire, _ := beego.AppConfig.Int("cache_expire")
	// 	err = cache.SetCache(cid, *v, cacheExpire)
	// 	if err != nil {
	// 		beego.Error(err)
	// 	}
	// }

	//		o.LoadRelated(v, "Video")
	//		o.LoadRelated(v, "Livechannel")
	//		beego.Debug(v.Organization)
	return v, nil

}

// GetAllUser retrieves all User matches certain condition. Returns empty list if
// no records exist
func GetAllUser(query map[string]interface{}, fields []string, sortby []string, order []string,
	offset int64, limit int64) (ml []interface{}, count int64, err error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(User))
	// query k=v
	for k, v := range query {
		// rewrite dot-notation to Object__Attribute
		k = strings.Replace(k, ".", "__", -1)
		qs = qs.Filter(k, v)
	}
	qs = qs.Filter("Isdelete", 0)
	// order by:
	var sortFields []string
	if len(sortby) != 0 {
		if len(sortby) == len(order) {
			// 1) for each sort field, there is an associated order
			for i, v := range sortby {
				orderby := ""
				if order[i] == "desc" {
					orderby = "-" + v
				} else if order[i] == "asc" {
					orderby = v
				} else {
					return nil, 0, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
			qs = qs.OrderBy(sortFields...)
		} else if len(sortby) != len(order) && len(order) == 1 {
			// 2) there is exactly one order, all the sorted fields will be sorted by this order
			for _, v := range sortby {
				orderby := ""
				if order[0] == "desc" {
					orderby = "-" + v
				} else if order[0] == "asc" {
					orderby = v
				} else {
					return nil, 0, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
		} else if len(sortby) != len(order) && len(order) != 1 {
			return nil, 0, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
		}
	} else {
		if len(order) != 0 {
			return nil, 0, errors.New("Error: unused 'order' fields")
		}
	}

	var l []orm.Params
	qs = qs.RelatedSel().OrderBy(sortFields...)
	count, err = qs.Count()
	if _, err = qs.Limit(limit, offset).Values(&l, fields...); err == nil {
		if len(fields) == 0 {
			for _, v := range l {

				u, _ := GetUserById(v["Id"].(string))
				beego.Debug(u.Role)
				if len(u.Role) > 0 {
					v["OrgName"] = u.Organization.Name
					v["RoleName"] = u.Role[0].Name
					v["RoleId"] = u.Role[0].Id
					v["Pname"] = u.Abbr
					v["DeptId"] = u.Dept[0].Id
					v["DeptName"] = u.Dept[0].Name
					v["Sname"] = params.GetStatus(u.Status)
					ml = append(ml, v)
				}

			}
		} else {
			// trim unused fields
			for _, v := range l {
				m := make(map[string]interface{})
				val := reflect.ValueOf(v)
				for _, fname := range fields {
					m[fname] = val.FieldByName(fname).Interface()
				}
				ml = append(ml, m)
			}
		}
		return ml, count, nil
	}
	beego.Error(" []orm.Params:", err)
	return nil, 0, err
}

// UpdateUser updates User by Id and returns error if
// the record to be updated doesn't exist
func UpdateUserById(m *User) (err error) {
	o := orm.NewOrm()
	_, err = o.Update(m)
	return
}

// DeleteUser deletes User by Id and returns error if
// the record to be deleted doesn't exist
func DeleteUser(id string) (err error) {
	o := orm.NewOrm()
	v := User{Id: id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Delete(&User{Id: id}); err == nil {
			fmt.Println("Number of records deleted in database:", num)
		}
		fmt.Println("error is :", err)
	}
	return
}

//SoftDeleteUser 软删除用户数据
//Param id string
//Param uname string
//return error
func SoftDeleteUser(id string, uname string) (err error) {
	o := orm.NewOrm()
	query := map[string]interface{}{
		"id": id,
	}
	_, err = SoftDelete(o, "sys_user", query, uname)
	return
}

// GetUserByUname 查询用户根据帐号
func GetUserByUname(uname string) (user *User, err error) {
	o := orm.NewOrm()
	user = &User{Name: uname}
	err = o.Read(user, "Name")
	//beego.Debug("user err is :", err)
	if err == orm.ErrNoRows {
		beego.Warning("\n 查询不到")
	} else if err == orm.ErrMissPK {
		beego.Warning("\n 找不到主键")
	} else {
		beego.Debug("user is :", user.Id, user.Name)
	}
	if _, err := o.LoadRelated(user, "Organization"); err != nil {
		return nil, err
	}
	if _, err := o.LoadRelated(user, "Role"); err != nil {
		return nil, err
	}
	if _, err = o.LoadRelated(user, "Dept"); err != nil {
		return nil, err
	}
	if user.Organization.Status == 0 || user.Organization.Isdelete == 1 {
		err = errors.New("无效用户")
		// user = nil
	}
	return
}

// GetUserNameExist 查询用户帐号是否存在
func GetUserNameExist(uname string) bool {
	o := orm.NewOrm()
	user := User{Name: uname}
	err := o.Read(&user, "Name")
	if err != nil {
		return false
	}
	return true

}

//GetAllUserList 获取所有单位的用户
//return []User,error
func GetAllUserList() ([]User, error) {
	o := orm.NewOrm()
	var userList []User
	sql := "SELECT * FROM public.sys_user ta WHERE ta.isdelete=0"
	_, err := o.Raw(sql).QueryRows(&userList)
	return userList, err
}

//Desc 根据单位获取用户
//Param orgId 单位Id
//return []User,error
func GetUserByOrgId(orgId int64) ([]User, error) {
	o := orm.NewOrm()
	var userList []User
	sql := "SELECT * FROM public.sys_user ta WHERE ta.isdelete=0 AND ta.organization_id =?"
	_, err := o.Raw(sql, orgId).QueryRows(&userList)
	return userList, err
}

//GetUserByOrg 根据单位获取有效用户
//Param orgId 单位Id
//return []orm.Params,error
func GetUserByOrg(orgId string) ([]orm.Params, error) {
	cid := "GetUserByOrg.id." + orgId
	var users []orm.Params
	err := cache.GetCache(cid, &users)
	if err != nil {
		o := orm.NewOrm()
		var userList []orm.Params
		qs := o.QueryTable(new(User)).Filter("Organization", orgId).RelatedSel().Filter("isdelete", 0).Filter("status", 1)
		qs.Limit(-1).Values(&userList)
		//	beego.Debug(orgId, userList)
		for _, v := range userList {
			u, _ := GetUserById(v["Id"].(string))
			v["OrgName"] = u.Organization.Name
			v["RoleName"] = u.Role[0].Name
			v["RoleId"] = u.Role[0].Id
			v["Pname"] = u.Abbr
			v["key"] = v["Id"]
			v["label"] = u.Abbr
			v["disabled"] = false
			v["DeptId"] = u.Dept[0].Id
			v["DeptName"] = u.Dept[0].Name
			v["Sname"] = params.GetStatus(u.Status)
			users = append(users, v)
		}
		cacheExpire, _ := beego.AppConfig.Int("cache_expire")
		err = cache.SetCache(cid, users, cacheExpire)
	}

	return users, err
}

//Desc 根据部门获取用户
//Param deptId 部门Id
//return []orm.Params,error
func GetUserByDept(deptId string) (userList []User, err error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(User)).Filter("Dept__SysDept__Id", deptId).RelatedSel().Filter("isdelete", 0).Filter("status", 1)
	qs.Limit(-1).All(&userList)
	return
}

//Desc 创建用户
//Param *User 用户
//Param *Userprof 用户属性
//Param roleId 用户角色
//Param deptId 用户部门
//return 用户id，error
func CreateUser(user *User, roleid string, deptid string) (*User, error) {
	//	var maps []orm.Params
	//	user.Id = libs.GetGuid()
	o := orm.NewOrm()
	err := o.Begin() // 事务处理过程
	_, err = o.Insert(user)
	beego.Debug(err)
	if err != nil {
		err = o.Rollback()
	}

	role := Role{Id: roleid}
	m2m := o.QueryM2M(user, "Role")
	_, err = m2m.Add(&role)
	beego.Debug(err)
	if err != nil {
		err = o.Rollback()
	}
	dept := Dept{Id: deptid}
	m2m = o.QueryM2M(user, "Dept")
	_, err = m2m.Add(&dept)
	beego.Debug(err)
	// 此过程中的所有使用 o Ormer 对象的查询都在事务处理范围内
	if err != nil {
		err = o.Rollback()
	}
	err = o.Commit()

	return user, err
}

//UpdateUser 修改用户
//Param *User 用户
//Param roleid 用户角色
//return error
func UpdateUser(user *User, roleid, deptid string) (err error) {
	//	var maps []orm.Params
	o := orm.NewOrm()
	err = o.Begin() // 事务处理过程
	_, err = o.Update(user)
	if err != nil {
		err = o.Rollback()
		return
	}
	role := Role{Id: roleid}
	beego.Debug(roleid)
	m2m := o.QueryM2M(&role, "User")
	err = DelUserRoleByUserId(user.Id)
	if err != nil {
		beego.Debug(err)
		err = o.Rollback()
		return
	}
	_, err = m2m.Add(user)
	if err != nil {
		err = o.Rollback()
		return
	}
	dept := Dept{Id: deptid}
	beego.Debug(deptid)
	m2m = o.QueryM2M(&dept, "User")
	err = DeleteUserDeptByUserId(user.Id)
	if err != nil {
		beego.Debug(err)
		err = o.Rollback()
		return
	}
	_, err = m2m.Add(user)
	if err != nil {
		err = o.Rollback()
		return
	}
	// 此过程中的所有使用 o Ormer 对象的查询都在事务处理范围内
	err = o.Commit()

	return err
}

//Desc 验证用户合法
//Param  用户id
//return User, error
func CheckUser(id string) bool {
	o := orm.NewOrm()
	qs := o.QueryTable(new(User)).Filter("id", id).Filter("Isdelete", 0).Filter("status", 1)
	return qs.Exist()

}

//GetUserByIds 验证用户合法
//Param  用户id
//return User, error
func GetUserByIds(ids []string) (users []*User) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(User)).Filter("Id__in", ids)
	qs.All(&users)
	return

}

//GetUserByDeptRoel 根据部门和角色获取用户
func GetUserByDeptRoel(deptId, rid string) (user *User, err error) {
	o := orm.NewOrm()
	var userList []User
	dps := new([]string)
	GetParentDept(deptId, dps)
	qs := o.QueryTable(new(User)).Filter("Dept__SysDept__Id__in", dps).Filter("Role__SysRole__Id", rid).RelatedSel().Filter("isdelete", 0).Filter("status", 1)
	ct, err := qs.Limit(-1).All(&userList)
	beego.Debug("org_id:", deptId, "rid:", rid)
	beego.Debug(ct)
	if ct == 1 {
		user = &userList[0]
	} else {
		err = errors.New("有效用户角色有问题")
	}
	return
}

func GetUserByDeptRoleRewrite(deptId, rid string) (user *User, err error) {
	o := orm.NewOrm()
	sql := `
SELECT t0.* FROM sys_user t0 
LEFT JOIN sys_user_sys_depts t1 ON t0."id" = t1.sys_user_id 
LEFT JOIN sys_user_sys_roles t2 ON t0."id" = t2.sys_user_id
WHERE t1.sys_dept_id = ? AND t2.sys_role_id = ?
AND t0.isdelete = 0 AND t0.status = 1`
	var users []User
	ct, err := o.Raw(sql, deptId, rid).QueryRows(&users)
	beego.Debug("org_id:", deptId, "rid:", rid)
	beego.Debug(ct)
	if ct == 1 {
		user = &users[0]
	} else {
		err = errors.New("有效用户角色有问题")
	}
	return
}

//GetUserByOrgRoel 根据组织单位和角色获取用户
func GetUserByOrgRoel(orgId, rid string) (user *User, err error) {
	o := orm.NewOrm()
	var userList []User
	// dps := new([]string)
	// GetParentDept(deptId, dps)
	qs := o.QueryTable(new(User)).Filter("Organization__Id", orgId).Filter("Role__SysRole__Id", rid).RelatedSel().Filter("isdelete", 0).Filter("status", 1)
	ct, err := qs.Limit(-1).All(&userList)
	beego.Debug("org_id:", orgId, "rid:", rid)
	beego.Debug(ct)
	if ct == 1 {
		user = &userList[0]
	} else {
		err = errors.New("有效用户角色有问题")
	}
	beego.Debug(user)
	return
}

func GetUserByOrgRoleRewrite(orgId, rid string) (user *User, err error) {
	o := orm.NewOrm()
	sql := `SELECT * FROM sys_user t0
LEFT JOIN sys_user_sys_roles t1 ON t0."id" =  t1.sys_user_id
WHERE t0.organization_id = ? AND t1.sys_role_id = ? 
AND t0.isdelete = 0 AND t0.status = 1`
	var users []User
	ct, err := o.Raw(sql, orgId, rid).QueryRows(&users)
	beego.Debug("orgId:", orgId, "rid:", rid)
	beego.Debug(ct)
	if ct == 1 {
		user = &users[0]
	} else {
		err = errors.New("有效用户角色有问题")
	}
	return
}

//GetUserByOrgRoel 根据组织单位和角色获取用户
func GetUserByOrgRoelList(orgId, rid string) (users []User, err error) {
	o := orm.NewOrm()
	var userList []User
	// dps := new([]string)
	// GetParentDept(deptId, dps)
	qs := o.QueryTable(new(User)).Filter("Organization__Id", orgId).Filter("Role__SysRole__Id", rid).RelatedSel().Filter("isdelete", 0).Filter("status", 1)
	ct, err := qs.Limit(-1).All(&userList)
	beego.Debug("org_id:", orgId, "rid:", rid)
	if ct == 0 {

		err = errors.New("有效用户角色有问题")
	} else {
		users = userList
	}
	//beego.Debug(user)
	return
}

//UpdateUserProf 修改用户属性
func UpdateUserProf(user *User) (err error) {
	//	var maps []orm.Params
	o := orm.NewOrm()
	_, err = o.Update(user)
	cid := "GetUserById.id." + user.Id
	err = cache.DelCache(cid)
	return
}

//Update 修改用户属性
func (u *User) Update(fields ...string) error {
	if _, err := orm.NewOrm().Update(u, fields...); err != nil {
		return err
	}
	return nil
}

// 根据单位和角色查询用户
// 由于beego orm的限制，sql查询不支持复杂的struct，所以使用 UserTmp
// 参照 https://github.com/astaxie/beego/issues/384
func GetUsersByOrgAndRole(orgId, roleId string) ([]UserTmp, error) {
	o := orm.NewOrm()
	beego.Debug("orgId, roleId", orgId, roleId)
	var l []UserTmp
	sql := `SELECT t0.* FROM sys_user t0 LEFT JOIN sys_user_sys_roles t1
ON t0."id"=t1.sys_user_id
WHERE
t0.isdelete = 0 AND t0.status=1 AND
t0.organization_id = ? AND t1.sys_role_id=?
`
	_, err := o.Raw(sql, orgId, roleId).QueryRows(&l)
	return l, err
}

// 由于beego orm的限制，sql查询不支持复杂的struct，
// 参照 https://github.com/astaxie/beego/issues/384
type UserTmp struct {
	Id   string
	Abbr string
}

func QueryUserByRoleNameAndDept(roleName, deptId string) ([]UserTmp, error) {
	o := orm.NewOrm()
	sql := `SELECT t0."id", t0.abbr FROM sys_user t0 
 LEFT JOIN sys_user_sys_roles t1 ON t0."id"=t1.sys_user_id
 LEFT JOIN sys_role t2 ON t1.sys_role_id = t2."id" 
 LEFT JOIN sys_user_sys_depts t3 ON t0."id" = t3.sys_user_id
 LEFT JOIN sys_dept t4 ON t3.sys_dept_id = t4."id"  
 WHERE t2."name" like ? AND t4."id"=? AND t0.isdelete=0 AND t0.status=1`
	var l []UserTmp
	var p = []string{"%", "%"}
	_, err := o.Raw(sql, strings.Join(p, roleName), deptId).QueryRows(&l)
	return l, err
}

func QueryLeaderUserByProfessorGroupId(o orm.Ormer, groupid string) (*User, error) {
	//o := orm.NewOrm()
	sql := `
SELECT t0.* FROM sys_user t0
LEFT JOIN ims_professor_group_sys_user t1 ON t0."id"= t1.user_id 
WHERE t1.professor_group_id = ? AND t1.is_leader=true`
	var user User
	err := o.Raw(sql, groupid).QueryRow(&user)
	return &user, err
}

func GetProfessorGroupUsers(groupId string) ([]UserTmp, error) {
	sql := `
SELECT t0.* FROM sys_user t0
LEFT JOIN ims_professor_group_sys_user t1 ON t0."id" = t1.user_id
WHERE t1."professor_group_id" = ? AND t0.isdelete=0 AND t0.status=1
`
	o := orm.NewOrm()
	var l []UserTmp
	_, err := o.Raw(sql, groupId).QueryRows(&l)
	return l, err
}
